package de.gwtprime.server.sql.helper;

import java.util.List;
import java.util.logging.Logger;

import javax.persistence.EntityManager;
import javax.persistence.EntityManagerFactory;
import javax.persistence.Query;

import org.hibernate.Session;
import org.hibernate.SessionFactory;
import org.hibernate.Transaction;

import de.gwtprime.server.model.Article;
import de.gwtprime.server.model.EntityBase;
import de.gwtprime.server.sql.HibernateEntityManagerSingeltonNew;
import de.gwtprime.server.sql.HibernateEntityManagerSingeltonNewStaging;
import de.gwtprime.server.sql.HibernateEntityMangerSingelton;
import de.gwtprime.server.sql.HibernateEntityMangerSingeltonTest;
import de.gwtprime.server.util.AppUtils;

public class HibernateHelper {

	/**
	 * Set to true for unit tests
	 */
	public static boolean test = false;

	private static Logger logger = Logger.getLogger(HibernateHelper.class
			.getName());

	public static Long persist(EntityBase entity){

		EntityManager entityManager = null;
		try {
			entityManager = beginTransaction();

			if(entity.getId()!=null){
				entityManager.merge(entity);
			}else{

				entityManager.persist(entity);
			}

			entityManager.getTransaction().commit();

		} catch (RuntimeException e) {
			logger.severe("Failed to insert into DB: "+ e);
			throw e;
		}finally{
			if(entityManager != null)
				entityManager.close();
		}

		logger.info("Object was added to the DB: " + entity.toString());
		return entity.getId();

	}


	public static Long persistWithOutEntityManager(EntityManager entityManager, EntityBase entity){
		try {
			entityManager.getTransaction().begin();
			if(entity.getId()!=null){
				entityManager.merge(entity);
			}else{

				entityManager.persist(entity);
			}
			entityManager.flush();
			entityManager.getTransaction().commit();

		} catch (RuntimeException e) {
			logger.severe("Failed to insert into DB: "+ e);
			throw e;
		}

		logger.info("Object was added to the DB: " + entity.toString());
		return entity.getId();

	}


	public static <T extends EntityBase> T merge(EntityManager entityManager, T entity){
		entityManager.getTransaction().begin();
		T ent =  entityManager.merge(entity);
		entityManager.flush();
		entityManager.getTransaction().commit();
		return ent;

	}

	public static <T extends EntityBase> T merge(T entity){

		EntityManager entityManager = null;
		try {
			entityManager = beginTransaction();
			entity = entityManager.merge(entity);
			entityManager.getTransaction().commit();

		} catch (Exception e) {
			logger.severe("Failed to merge entity: "+ e);
			return null;
		}finally{
			if(entityManager != null)
				entityManager.close();
		}

		logger.info("Object was merged to the DB: " + entity.toString());
		return entity;

	}

	public  static<T> T find(Class<T> myClass, Object id){
		logger.info(String.format("Find object %s with id=%s", myClass.toString(), id));
		EntityManager entityManager = beginTransaction();
		T obj = entityManager.find(myClass, id);
		return obj;
	}


	public static <T extends EntityBase> void  persistNew(T entity) {
		SessionFactory sessionFacotry = null;

		if(!AppUtils.isAppEngine()){
			sessionFacotry = HibernateEntityManagerSingeltonNewStaging.getSessionFactory();
		}else{
			sessionFacotry = HibernateEntityManagerSingeltonNew.getSessionFactory();
		}

		try{
			Session session = sessionFacotry.getCurrentSession();
			Transaction tx = session.beginTransaction();

			session.saveOrUpdate(entity);
			tx.commit();
		}catch(RuntimeException e){

			logger.severe("Error on persistNew: "+ e);
			sessionFacotry.getCurrentSession().getTransaction().rollback();
			throw e;
		}
		//session.close();

		logger.info("Object was added to the DB: " + entity.toString());

	}

	public static <T extends EntityBase>void remove(T entity) {
		EntityManager entityManager = null;
		try {
			entityManager = beginTransaction();
			// first a lookup is needed -> prevents "detached" error
			EntityBase newEnt = entityManager.find(entity.getClass(), entity.getId());

			entityManager.remove(newEnt);
			entityManager.getTransaction().commit();
		} catch (Exception e) {
			logger.severe("Failed to remove from DB: "+ e);
			return;
		}finally{
			if(entityManager!=null)
				entityManager.close();
		}

		logger.info("Object was removed from the DB: " + entity.toString());

	}

	public static <T> Object getLatestObjectForClass(T myclass) {

		EntityManager entityManager = beginTransaction();
		Query query = entityManager.createQuery(String.format(
				"from %s order by(id) desc", ((Class<?>) myclass).getName()));
		query.setMaxResults(1);

		@SuppressWarnings("unchecked")
		List<T> resultList = (List<T>) query.getResultList();

		logger.info(resultList.toString());
		entityManager.getTransaction().commit();
		entityManager.close();

		if (resultList.size() > 0) {
			return resultList.get(0);
		}

		return null;
	}

	public static <T extends EntityBase> T queryDB(T myclass, String queryString) {

		EntityManager entityManager = beginTransaction();
		Query query = entityManager.createQuery(queryString);
		query.setMaxResults(1);

		@SuppressWarnings("unchecked")
		List<T> resultList = (List<T>) query.getResultList();

		logger.info(resultList.toString());
		entityManager.getTransaction().commit();
		entityManager.close();

		if (resultList.size() > 0) {
			return resultList.get(0);
		}

		return null;
	}

	
	public static <T extends EntityBase> List<T> queryDBForList(T myclass, String queryString) {
		return queryDBForList(myclass, queryString, 0);
	}
	
	public static <T extends EntityBase> List<T> queryDBForList(T myclass, String queryString, int maxNumberResults) {

		EntityManager entityManager = beginTransaction();
		Query query = entityManager.createQuery(queryString);

		if(maxNumberResults!=0)
			query.setMaxResults(maxNumberResults);
		@SuppressWarnings("unchecked")
		List<T> resultList = (List<T>) query.getResultList();

		logger.info(resultList.toString());
		entityManager.getTransaction().commit();
		entityManager.close();

		return resultList;
	}

	public static List<Article> getLatestArticles(int numberArticles) {
		EntityManager entityManager = beginTransaction();

		Query query = entityManager.createQuery(String.format(
				"from %s where published=1 order by id desc", Article.class.getName()));
		query.setMaxResults(numberArticles);

		@SuppressWarnings("unchecked")
		List<Article> list = (List<Article>) query.getResultList();

		entityManager.close();
		return list;
	}

	public static EntityManager beginTransaction() {

		EntityManagerFactory entityManagerFactory = null;
		EntityManager entityManager = null;
		try{
			if(!AppUtils.isAppEngine()){
				entityManagerFactory = HibernateEntityMangerSingeltonTest
						.getEntityManagerFactory();
			}else{
				entityManagerFactory = HibernateEntityMangerSingelton
						.getEntityManagerFactory();
			}
			entityManager = entityManagerFactory
					.createEntityManager();
			entityManager.getTransaction().begin();

		}catch(Exception e){
			logger.severe("Failed to initialize EntityManagerFactory: "+e);
		}
		return entityManager;
	}

	public static EntityManager beginTransactionWithoutTransaction() {


		EntityManagerFactory entityManagerFactory = null;
		EntityManager entityManager = null;

		try{
			if(!AppUtils.isAppEngine()){
				entityManagerFactory = HibernateEntityMangerSingeltonTest
						.getEntityManagerFactory();
			}else{
				entityManagerFactory = HibernateEntityMangerSingelton
						.getEntityManagerFactory();
			}
			entityManager = entityManagerFactory
					.createEntityManager();

		}catch(Exception e){
			logger.severe("Failed to initialize EntityManagerFactory: "+e);
		}
		return entityManager;
	}

}
